package com.zp.ojcodesandbox.util;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.zp.ojcodesandbox.model.ExecuteMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

import java.io.*;
import java.util.concurrent.TimeUnit;

@Slf4j
public class ProcessRunAndMessage {
    private static final Long TIME_OUT = 3000L;

    public static ExecuteMessage execute(Process runProcess, String opName){
        ExecuteMessage executeMessage = new ExecuteMessage();
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            Integer compliedCode = runProcess.waitFor();
            executeMessage.setMessageId(compliedCode);
            if(compliedCode == 0){
                System.out.println( opName + " successful");
                // 读取输出信息
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream(), "utf-8"));
                String inputLine;
                StringBuilder inputBuilder = new StringBuilder();
                while ((inputLine = bufferedReader.readLine()) != null){
                    System.out.println(inputLine);
                    inputBuilder.append(inputLine).append("\n");
                }
                executeMessage.setInputMessage(inputBuilder.toString());
                bufferedReader.close();
            }else {
                System.out.println(opName + " error:" + compliedCode);
                // 读取输出信息
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream(), "utf-8"));
                String inputLine;
                StringBuilder inputBuilder = new StringBuilder();
                while ((inputLine = bufferedReader.readLine()) != null){
                    System.out.println(inputLine);
                    inputBuilder.append(inputLine).append("\n");
                }
                executeMessage.setInputMessage(inputBuilder.toString());
                // 错误信息
                BufferedReader errorReader = new BufferedReader(new InputStreamReader(runProcess.getErrorStream(), "gbk"));
                StringBuilder errorBuilder = new StringBuilder();
                String errorLine;
                while ((errorLine = errorReader.readLine()) != null){
                    System.out.println(errorLine);
                    errorBuilder.append(errorLine).append("\n");
                }
                // 结束所有输出溜
                bufferedReader.close();
                errorReader.close();
                executeMessage.setErrorMessage(errorBuilder.toString());
            }
            // 获取运行时间
            stopWatch.stop();
            executeMessage.setTime(stopWatch.getLastTaskTimeMillis());
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return executeMessage;
    }

    public static ExecuteMessage interactExecute(Process runProcess, String opName, String args){
        ExecuteMessage executeMessage = new ExecuteMessage();
        try {
            InputStream inputStreamReader = runProcess.getInputStream();
            OutputStreamWriter outputStream = new OutputStreamWriter(runProcess.getOutputStream());

            outputStream.write(args);
            int compliedCode = runProcess.waitFor();
            executeMessage.setMessageId(compliedCode);
            if(compliedCode == 0){
                System.out.println( opName + " successful");
                // 读取输出信息
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()));
                String inputLine;
                StringBuilder inputBuilder = new StringBuilder();
                while ((inputLine = bufferedReader.readLine()) != null){
                    inputBuilder.append(inputLine);
                }
                executeMessage.setInputMessage(inputBuilder.toString());
            }else {
                System.out.println(opName + " error:" + compliedCode);
                // 读取输出信息
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()));
                String inputLine;
                StringBuilder inputBuilder = new StringBuilder();
                while ((inputLine = bufferedReader.readLine()) != null){
                    inputBuilder.append(inputLine);
                }
                executeMessage.setInputMessage(inputBuilder.toString());
                // 错误信息
                BufferedReader errorReader = new BufferedReader(new InputStreamReader(runProcess.getErrorStream()));
                StringBuilder errorBuilder = new StringBuilder();
                String errorLine;
                while ((errorLine = errorReader.readLine()) != null){
                    errorBuilder.append(errorLine);
                }
                executeMessage.setErrorMessage(errorBuilder.toString());
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return executeMessage;
    }
    // docker 执行程序
    public static ExecuteMessage dockerExecute(DockerClient dockerClient, String[] exeCmd, String option, String containerId, Boolean getMeomry) {
        ExecuteMessage executeMessage = new ExecuteMessage();
        final StringBuilder inputMessage = new StringBuilder();
        final StringBuilder errorMessage = new StringBuilder();
        Long time = 0L;
        //在容器内编译
        // javac -encoding utf-8 app/Main.java

        // 获取容器命令行权限
        ExecCreateCmd execCreateCmd = dockerClient.execCreateCmd(containerId);
        // 配置命令行可使用资源
        ExecCreateCmdResponse compiledResponse = execCreateCmd
                .withCmd(exeCmd)
                .withAttachStderr(true)
                .withAttachStdout(true)
                .withAttachStdin(true)
                .exec();

        // 设置执行需要返回的信息
        ExecStartResultCallback execStartResultCallback = new ExecStartResultCallback() {
            @Override
            public void onNext(Frame frame) {
                StreamType type = frame.getStreamType();
                if (StreamType.STDERR.equals(type)) {
                    errorMessage.append(new String(frame.getPayload()) + "\n");
                    log.info(option +"失败" + new String(frame.getPayload()));
                } else {
                    inputMessage.append(new String(frame.getPayload()) + "\n");
                    log.info(option +"成功 " + new String(frame.getPayload()));
                }
                super.onNext(frame);
            }
        };
        // 获取内存占用
        final Long[] memoryMax = {0L};
        // StatsCmd statsCmd = dockerClient.statsCmd(containerId);
        // 不需要获取就不返回
        StatsCmd statsCmd = dockerClient.statsCmd(containerId);
        if(getMeomry) {
            ResultCallback<Statistics> statisticsResultCallback = statsCmd.exec(new ResultCallback<Statistics>() {
                @Override
                public void onStart(Closeable closeable) {

                }

                @Override
                public void onNext(Statistics statistics) {

                    log.info("内存占用: " + statistics.getMemoryStats().getUsage());
                    memoryMax[0] = Math.max(memoryMax[0], statistics.getMemoryStats().getUsage());
                }

                @Override
                public void onError(Throwable throwable) {

                }

                @Override
                public void onComplete() {

                }

                @Override
                public void close() throws IOException {

                }
            });
            statsCmd.exec(statisticsResultCallback);
            executeMessage.setMemory(memoryMax[0]);
        }
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            dockerClient.execStartCmd(compiledResponse.getId())
                    .exec(execStartResultCallback)
                    .awaitCompletion(TIME_OUT, TimeUnit.MILLISECONDS);
            // 关闭内存获取
            if(getMeomry) statsCmd.close();
            // 关闭时间
            stopWatch.stop();
            time = stopWatch.getLastTaskTimeMillis();
            System.out.println(option + "时间: " + time);
        } catch (InterruptedException e) {
            System.out.println("程序崩溃");
            throw new RuntimeException(e);
        }
        executeMessage.setMemory(memoryMax[0]);
        executeMessage.setInputMessage(inputMessage.toString());
        executeMessage.setErrorMessage(errorMessage.toString());
        executeMessage.setTime(time);
        return executeMessage;
    }

    // docker 执行程序
    public static ExecuteMessage dockerExecute(DockerClient dockerClient, String[] exeCmd, String[] inputs, String option, String containerId, Boolean getMeomry) {
        ExecuteMessage executeMessage = new ExecuteMessage();
        final StringBuilder inputMessage = new StringBuilder();
        final StringBuilder errorMessage = new StringBuilder();
        Long time = 0L;
        //在容器内编译
        // javac -encoding utf-8 app/Main.java

        // 获取容器命令行权限
        ExecCreateCmd execCreateCmd = dockerClient.execCreateCmd(containerId);
        // 配置命令行可使用资源
        ExecCreateCmdResponse compiledResponse = execCreateCmd
                .withCmd(exeCmd)
                .withTty(true)
                .withCmd(inputs)
                .withAttachStderr(true)
                .withAttachStdout(true)
                .withAttachStdin(true)
                .exec();

        // 设置执行需要返回的信息
        ExecStartResultCallback execStartResultCallback = new ExecStartResultCallback() {
            @Override
            public void onNext(Frame frame) {
                StreamType type = frame.getStreamType();
                if (StreamType.STDERR.equals(type)) {
                    errorMessage.append(new String(frame.getPayload()) + "\n");
                   log.info(option +"失败" + new String(frame.getPayload()));
                } else {
                    inputMessage.append(new String(frame.getPayload()) + "\n");
                    log.info(option +"成功 " + new String(frame.getPayload()));
                }
                super.onNext(frame);
            }
        };
        // 获取内存占用
        final Long[] memoryMax = {0L};
        // StatsCmd statsCmd = dockerClient.statsCmd(containerId);
        // 不需要获取就不返回
        StatsCmd statsCmd = dockerClient.statsCmd(containerId);
        if(getMeomry) {
            ResultCallback<Statistics> statisticsResultCallback = statsCmd.exec(new ResultCallback<Statistics>() {
                @Override
                public void onStart(Closeable closeable) {

                }

                @Override
                public void onNext(Statistics statistics) {

                    log.info("内存占用: " + statistics.getMemoryStats().getUsage());
                    memoryMax[0] = Math.max(memoryMax[0], statistics.getMemoryStats().getUsage());
                }

                @Override
                public void onError(Throwable throwable) {

                }

                @Override
                public void onComplete() {

                }

                @Override
                public void close() throws IOException {

                }
            });
            statsCmd.exec(statisticsResultCallback);
            executeMessage.setMemory(memoryMax[0]);
        }
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            dockerClient.execStartCmd(compiledResponse.getId())
                    .exec(execStartResultCallback)
                    .awaitCompletion(TIME_OUT, TimeUnit.MILLISECONDS);
            // 关闭内存获取
            if(getMeomry) statsCmd.close();
            // 关闭时间
            stopWatch.stop();
            time = stopWatch.getLastTaskTimeMillis();
            System.out.println(option + "时间: " + time);
        } catch (InterruptedException e) {
            System.out.println("程序崩溃");
            throw new RuntimeException(e);
        }
        executeMessage.setMemory(memoryMax[0]);
        executeMessage.setInputMessage(inputMessage.toString());
        executeMessage.setErrorMessage(errorMessage.toString());
        if(getMeomry) executeMessage.setTime(time);
        else executeMessage.setTime(0L);
        return executeMessage;
    }

}
